﻿// ********************************************************************************************************
// <copyright file="frmTableEditor.cs" company="TopX Geo-ICT">
//     Copyright (c) 2011 TopX Geo-ICT. All rights reserved.
// </copyright>
// ********************************************************************************************************
// The contents of this file are subject to the Mozilla Public License Version 1.1 (the "License"); 
// you may not use this file except in compliance with the License. You may obtain a copy of the License at 
// http:// www.mozilla.org/MPL/ 
// Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF 
// ANY KIND, either express or implied. See the License for the specificlanguage governing rights and 
// limitations under the License. 
// 
// The Initial Developer of this version is Jeen de Vegt.
// 
// Contributor(s): (Open source contributors should list themselves and their modifications here). 
// Change Log: 
// Date           Changed By      Notes
// 29 March 2012  Jeen de Vegt    Inital coding
// ********************************************************************************************************
namespace TableEditorNew.Forms
{
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;
    using System.Windows.Forms;

    using MapWinGIS;

    using TableEditorNew.BO;
    using TableEditorNew.Utils;

    /// <summary>
    ///  Form-class which show the shape-data
    /// </summary>
    public partial class frmTableEditor : Form
    {
        /// <summary>The BOMapWindow-object</summary> 
        private BOMapWindow boMapWindow;

        /// <summary>The selected columnindex</summary> 
        private int selectColumnIndex;

        /// <summary>
        /// The phase of the residual workflow
        /// </summary>
        private int rwf_phase;

        /// <summary>Initializes a new instance of the frmTableEditor class</summary>
        /// <param name = "boSF">The BOShapeFile-object.</param>
        /// <param name = "mapWindow">The BOMapWindow-object.</param>
        public frmTableEditor(int rwf_phase2, BOShapeFile boSF, BOMapWindow mapWindow)
        {
            InitializeComponent();

            this.rwf_phase = rwf_phase2;
            this.boShapeFile = boSF;
            this.boMapWindow = mapWindow;
        }

        /// <summary>Gets or sets the BOShapeFile-object</summary> 
        public BOShapeFile boShapeFile { get; set; }

        /// <summary>Initializes the form</summary>
        public void InitForm()
        {
            this.SetTitle();

            this.SetDataGrid();

            this.TableEditorDataGrid.SelectionChanged += this.TableEditorDataGrid_SelectionChanged;
        }

        /// <summary>Fill the datagrid with shape-data</summary>
        public void SetDataGrid()
        {
            BOShapeData boShapeData = this.boShapeFile.ShapeData;
            DataTable shapeFileData = boShapeData.GetDataTable();

            TableEditorDataGrid.DataSource = shapeFileData;
            //TableEditorDataGrid.ReadOnly = this.boShapeFile.IsReadOnly;
            TableEditorDataGrid.ReadOnly = false;
            TableEditorDataGrid.Columns[0].Visible = false;
            TableEditorDataGrid.Columns[1].ReadOnly = true;
            TableEditorDataGrid.Columns[1].Width = 130;
            if (rwf_phase == Velsto.Constants.RWF_PHASE_MARKERS)
            {
                TableEditorDataGrid.Columns[1].HeaderText = "Original Marker depth\n(not editable)";
            }
            else if (rwf_phase == Velsto.Constants.RWF_PHASE_RESID)
            {
                TableEditorDataGrid.Columns[1].HeaderText = "Original Residual depth\n(not editable)";
            }
            TableEditorDataGrid.Columns[1].ToolTipText = "These are the original depths as in your Marker file";
            TableEditorDataGrid.Columns[2].Width = 120;
            if (rwf_phase == Velsto.Constants.RWF_PHASE_MARKERS)
            {
                TableEditorDataGrid.Columns[2].HeaderText = "Marker depth adjusted";
            }
            else if (rwf_phase == Velsto.Constants.RWF_PHASE_RESID)
            {
                TableEditorDataGrid.Columns[2].HeaderText = "Residual depth adjusted";
            } 
            TableEditorDataGrid.Columns[3].Width = 110;
            TableEditorDataGrid.Columns[3].HeaderText = "Well name";
            TableEditorDataGrid.Columns[3].ReadOnly = false;
            btnApply.Visible = !this.boShapeFile.IsReadOnly;

            for (int i = 1; i < shapeFileData.Columns.Count; i++)
            {
                TableEditorDataGrid.Columns[i].Visible = !(bool)shapeFileData.Columns[i].ExtendedProperties["removed"];
            }

            this.SetSelected();
        }

        /// <summary>Set the selected shapes in the grid equal to the map</summary>
        public void SetSelected()
        {
            if (((DataTable)TableEditorDataGrid.DataSource).DefaultView.RowFilter == string.Empty)
            {
                // No filter
                for (int j = 0; j < this.boShapeFile.ShapeFile.NumShapes; j++)
                {
                    TableEditorDataGrid.Rows[j].Selected = this.boShapeFile.ShapeFile.get_ShapeSelected(j);
                }
            }
            else
            {
                string newRowfilter = this.CreateSelectedFilterFromShapeFile();

                ((DataTable)TableEditorDataGrid.DataSource).DefaultView.RowFilter = newRowfilter;

                this.SetAllRows(true);
            }

            lblAmountSeleted.Text = string.Format("{0} of {1} selected.", TableEditorDataGrid.SelectedRows.Count, TableEditorDataGrid.Rows.Count);
        }

        /// <summary>Create a filter which retreives the selected shapes</summary>
        /// <returns>The filter</returns>
        private string CreateSelectedFilterFromShapeFile()
        {
            StringBuilder query = new StringBuilder();
            query.Append("(");

            for (int j = 0; j < this.boShapeFile.ShapeFile.NumShapes; j++)
            {
                if (this.boShapeFile.ShapeFile.get_ShapeSelected(j))
                {
                    query.Append(j.ToString()).Append(",");
                }
            }

            query.Remove(query.Length - 1, 1).Append(")");

            string newRowfilter = "1 = 3";
            if (query.Length > 2)
            {
                newRowfilter = string.Format("SHAPE__ID IN {0}", query.ToString());
            }

            return newRowfilter;
        }

        /// <summary>Select or deselect all shapes</summary>
        /// <param name = "selected">Indicates if rows have to be selected or deselected.</param>
        private void SetAllRows(bool selected)
        {
            foreach (DataGridViewRow row in TableEditorDataGrid.Rows)
            {
                row.Selected = selected;
            }
        }

        /// <summary>Set the title of the form</summary>
        private void SetTitle()
        {
            this.Text += " - " + this.boShapeFile.ShapefileName;

            // Check if shape is readonly
            if (this.boShapeFile.IsReadOnly)
            {
                this.Text += " [ReadOnly]";
            }
        }

        /// <summary>Save changed data</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void btnClose_Click(object sender, EventArgs e)
        {
            this.CheckAndSaveChanges();

            this.Close();
        }

        /// <summary>Check if data is changed and save data if necessary</summary>
        /// <returns>Value indicating if saving was successfull</returns>
        public bool CheckAndSaveChanges()
        {
            bool noChangesOrUpdated = true;

            DataTable dt = (DataTable)TableEditorDataGrid.DataSource;

            if (this.boShapeFile.ShapeData.DataChanged(dt))
            {
                DialogResult dialogResult = FormUtils.TopMostMessageBox("Save changes?", "TableEditor", MessageBoxButtons.YesNo);
                if (dialogResult == DialogResult.Yes)
                {
                    this.Save();
                }
                else
                {
                    noChangesOrUpdated = false;
                }
            }

            return noChangesOrUpdated;
        }

        /// <summary>Save changed data</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void btnApply_Click(object sender, EventArgs e)
        {
            this.Save();
        }

        /// <summary>Save changed data</summary>
        private void Save()
        {
            DataTable dt = (DataTable)TableEditorDataGrid.DataSource;
            if (dt != null)
            {
                this.boShapeFile.ShapeData.SaveData(dt);

                this.SetDataGrid();
                int[] ia1 = { 0 };
                //((MapWinGIS.Shapefile)boShapeFile.ShapeFile).StartEditingShapes(true, null);
                this.boMapWindow.UpdateMap((MapWinGIS.Shapefile) boShapeFile.ShapeFile);
            }
        }

        /// <summary>Open form to add new field</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void addFieldToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.boShapeFile.IsReadOnly)
            {
                MessageBox.Show("Shapefile is readonly.");
            }
            else
            {
                DataTable dt = TableEditorDataGrid.DataSource as DataTable;

                frmNewField frm = new frmNewField(dt);
                frm.TopMost = true;
                frm.ShowDialog();
            }
        }

        /// <summary>Open form to remove field</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void removeFieldToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.boShapeFile.IsReadOnly)
            {
                MessageBox.Show("Shapefile is readonly.");
            }
            else
            {
                DataTable dt = TableEditorDataGrid.DataSource as DataTable;

                frmDeleteField frm = new frmDeleteField(dt);
                frm.TopMost = true;

                if (frm.ShowDialog() == System.Windows.Forms.DialogResult.OK)
                {
                    for (int i = 1; i < dt.Columns.Count; i++)
                    {
                        if ((bool)dt.Columns[i].ExtendedProperties["removed"])
                        {
                            TableEditorDataGrid.Columns[i].Visible = false;
                        }
                    }
                }
            }
        }

        /// <summary>Open form to rename field</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void renameFieldToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.boShapeFile.IsReadOnly)
            {
                MessageBox.Show("Shapefile is readonly.");
            }
            else
            {
                DataTable dt = TableEditorDataGrid.DataSource as DataTable;

                frmRenameField frm = new frmRenameField(dt);
                frm.TopMost = true;

                frm.ShowDialog();
            }
        }

        /// <summary>Show error</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void TableEditorDataGrid_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            if (e.Exception != null)
            {
                MessageBox.Show(e.Exception.Message);
                DataTable dt = (DataTable)TableEditorDataGrid.DataSource;
                dt.Rows[e.RowIndex][e.ColumnIndex] = System.DBNull.Value;
            }
        }

        /// <summary>Update map if selection is changed in grid</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void TableEditorDataGrid_SelectionChanged(object sender, EventArgs e)
        {
            DataGridViewSelectedRowCollection rowCollection = TableEditorDataGrid.SelectedRows;

            lblAmountSeleted.Text = string.Format("{0} of {1} selected.", rowCollection.Count, TableEditorDataGrid.Rows.Count);

            int[] indices = new int[rowCollection.Count];

            for (int i = 0; i < rowCollection.Count; i++)
            {
                int index = Convert.ToInt32(rowCollection[i].Cells[0].Value);
                indices[i] = index;
            }

            //this.boMapWindow.UpdateMap(indices);
        }

        /// <summary>Zoom to selected shape(s)</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void btnZoomToSelected_Click(object sender, EventArgs e)
        {
            this.ZoomToSelected();
        }

        /// <summary>Zoom to selected shape(s)</summary>
        private void ZoomToSelected()
        {
            if (TableEditorDataGrid.SelectedRows.Count > 0)
            {
                DataTable dt = TableEditorDataGrid.DataSource as DataTable;

                if ((this.boShapeFile.ShapeFile.ShapefileType == ShpfileType.SHP_POINT || this.boShapeFile.ShapeFile.ShapefileType == ShpfileType.SHP_MULTIPOINT)
                        && TableEditorDataGrid.SelectedRows.Count == 1)
                {
                    this.boMapWindow.MoveToSelected(TableEditorDataGrid.SelectedRows, this.boShapeFile.ShapeFile);
                }
                else
                {
                    //this.boMapWindow.ZoomToSelected(TableEditorDataGrid.SelectedRows, this.boShapeFile.ShapeFile);
                }
            }
        }

        /// <summary>Set selected shapes in grid</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void btnShowSelected_Click(object sender, EventArgs e)
        {
            this.ShowSelected();
        }

        /// <summary>Set selected shapes in grid</summary>
        private void ShowSelected()
        {
            // Disable grid-changed event
            this.TableEditorDataGrid.SelectionChanged -= new EventHandler(this.TableEditorDataGrid_SelectionChanged);

            if (((DataTable)TableEditorDataGrid.DataSource).DefaultView.RowFilter == string.Empty)
            {
                // Only show selected records
                string newRowfilter = this.CreateSelectedFilterFromGrid(TableEditorDataGrid.SelectedRows);

                // Apply filter on grid
                ((DataTable)TableEditorDataGrid.DataSource).DefaultView.RowFilter = newRowfilter;

                // Select all rows
                this.SetAllRows(true);
            }
            else
            {
                // Remove filter from grid
                ((DataTable)TableEditorDataGrid.DataSource).DefaultView.RowFilter = string.Empty;

                // Select rows in grid again
                for (int j = 0; j < this.boShapeFile.ShapeFile.NumShapes; j++)
                {
                    TableEditorDataGrid.Rows[j].Selected = this.boShapeFile.ShapeFile.get_ShapeSelected(j);
                }
            }
        }

        /// <summary>Create a filter based on selectedrow(s)</summary>
        /// <param name = "selectedRows">The selected rows.</param>
        /// <returns>The filter</returns>
        private string CreateSelectedFilterFromGrid(DataGridViewSelectedRowCollection selectedRows)
        {
            string newRowfilter = string.Empty;

            if (selectedRows != null && selectedRows.Count != 0)
            {
                StringBuilder query = new StringBuilder();
                query.Append("(");

                foreach (DataGridViewRow row in selectedRows)
                {
                    query.Append(row.Cells[0].Value.ToString()).Append(",");
                }

                query.Remove(query.Length - 1, 1).Append(")");

                newRowfilter = string.Format("SHAPE__ID IN {0}", query.ToString());
            }
            else
            {
                newRowfilter = "1 = 3";
            }

            return newRowfilter;
        }

        /// <summary>Zoom to selected/edited shape</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuZoomToEdited_Click(object sender, EventArgs e)
        {
            this.ZoomToEdit();
        }

        /// <summary>Zoom to selected/edited shape</summary>
        private void ZoomToEdit()
        {
            this.boMapWindow.ZoomToEdit(Convert.ToInt32(TableEditorDataGrid.CurrentRow.Cells[0].Value), this.boShapeFile.ShapeFile);
        }

        /// <summary>Zoom to selected shapes</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuZoomToSelected_Click(object sender, EventArgs e)
        {
            this.ZoomToSelected();
        }

        /// <summary>Show selected shapes</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuShowSelected_Click(object sender, EventArgs e)
        {
            this.ShowSelected();
        }

        /// <summary>Select all shapes in grid</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuSelectAll_Click(object sender, EventArgs e)
        {
            this.SetAllRows(true);
        }

        /// <summary>Deselect all shapes in grid</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuSelectNone_Click(object sender, EventArgs e)
        {
            this.DesSelectAllRows();
        }

        /// <summary>Deselect all shapes in grid</summary>
        private void DesSelectAllRows()
        {
            foreach (DataGridViewRow row in TableEditorDataGrid.Rows)
            {
                row.Selected = false;
            }
        }

        /// <summary>Invert selected shapes in grid</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuInvertSelection_Click(object sender, EventArgs e)
        {
            this.InvertSelection();
        }

        /// <summary>Invert selected shapes in grid</summary>
        private void InvertSelection()
        {
            foreach (DataGridViewRow row in TableEditorDataGrid.Rows)
            {
                row.Selected = !row.Selected;
            }
        }

        /// <summary>Export features</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuExportFeatures_Click(object sender, EventArgs e)
        {
            // Check if data has changed
            if (this.CheckAndSaveChanges())
            {
                this.boMapWindow.ExportShapes(this.boShapeFile.ShapeFile);
            }
            else
            {
                MessageBox.Show("No data exported. Changes must be saved first.");
            }
        }

        /// <summary>Perform a search</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuFind_Click(object sender, EventArgs e)
        {
            string value = string.Empty;
            if (FormUtils.InputBox("Find", "Search:", ref value) == DialogResult.OK && value != string.Empty)
            {
                for (int i = 0; i < TableEditorDataGrid.Rows.Count; i++)
                {
                    for (int j = 0; j < TableEditorDataGrid.Columns.Count; j++)
                    {
                        try
                        {
                            string cellValue = TableEditorDataGrid[j, i].Value.ToString();

                            TableEditorDataGrid.Rows[i].Cells[j].Selected = cellValue.ToLower().Contains(value);
                        }
                        catch
                        {
                        }
                    }
                }

                if (TableEditorDataGrid.SelectedCells.Count == 0)
                {
                    MessageBox.Show("No results found.");
                }
            }
        }

        /// <summary>Check if a given value is valid for columntype</summary>
        /// <param name = "columnId">the column.</param>
        /// <param name = "value">The value to check.</param>
        /// <returns>Value indicating if value is valid</returns>
        private bool CheckCurrentColumn(int columnId, string value)
        {
            bool checkCurrentColomn = true;

            // Get column type
            Type type = TableEditorDataGrid[columnId, 0].ValueType;

            if (type == typeof(int))
            {
                checkCurrentColomn = NummericHelper.IsNumeric(value, System.Globalization.NumberStyles.Integer);
            }
            else if (type == typeof(double))
            {
                checkCurrentColomn = NummericHelper.IsNumeric(value, System.Globalization.NumberStyles.Float);
            }

            return checkCurrentColomn;
        }

        /// <summary>Perform a replace</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuReplace_Click(object sender, EventArgs e)
        {
            string search = string.Empty;
            string replace = string.Empty;
            int totalChanged = 0;

            if (FormUtils.ReplaceBox(ref search, ref replace) == DialogResult.OK && search != string.Empty)
            {
                for (int j = 1; j < TableEditorDataGrid.Columns.Count; j++)
                {
                    bool checkCurrentColomn = this.CheckCurrentColumn(j, replace);

                    if (checkCurrentColomn)
                    {
                        for (int i = 0; i < TableEditorDataGrid.Rows.Count; i++)
                        {
                            try
                            {
                                string cellValue = TableEditorDataGrid[j, i].Value.ToString();
                                if (cellValue.ToLower().Contains(search.ToLower()))
                                {
                                    ///////
                                    // There seems to be a bug in the datatable, if only the first record is changed it doesn't
                                    // get the status modified, so force it here!!

                                    DataTable dt = (DataTable)TableEditorDataGrid.DataSource;

                                    if (i == 0 && dt.Rows[0].RowState != DataRowState.Modified)
                                    {
                                        dt.Rows[0].SetModified();
                                    }

                                    //////

                                    TableEditorDataGrid[j, i].Value = Regex.Replace(cellValue, search, replace, RegexOptions.IgnoreCase);
                                    totalChanged++;
                                }
                            }
                            catch
                            {
                            }
                        }
                    }
                }
            }

            MessageBox.Show(string.Format("{0} replacements were made.", totalChanged));
        }

        /// <summary>Filter gridview</summary>
        /// <param name = "queryString">The query to perform.</param>
        public void Query(string queryString)
        {
            DataTable dt = (DataTable)TableEditorDataGrid.DataSource;

            try
            {
                DataRow[] foundRows = dt.Select(queryString);

                if (foundRows.Length > 0)
                {
                    frmUpdateSelection updateSelection = new frmUpdateSelection(foundRows.Length);
                    DialogResult result = updateSelection.ShowDialog();

                    if (result == DialogResult.OK)
                    {
                        MapWindow.Interfaces.SelectionOperation operation = updateSelection.selectedOption;

                        List<int> selectedIds = foundRows.Select(elm => Convert.ToInt32(elm[0])).ToList();

                        this.UpdateFromQuerySelection(operation, selectedIds);
                    }
                }
                else
                {
                    this.SetAllRows(false);
                }
            }
            catch (EvaluateException)
            {
                MessageBox.Show("The query you have entered is not valid. Please adjust your query syntax");
                return;
            }
        }

        /// <summary>Exectue a query</summary>
        /// <param name = "operation">The operation to perform.</param>
        /// <param name = "selectedIds">The list of selecteds shapes to perform the operation on.</param>
        private void UpdateFromQuerySelection(MapWindow.Interfaces.SelectionOperation operation, List<int> selectedIds)
        {
            switch (operation)
            {
                case MapWindow.Interfaces.SelectionOperation.SelectNew:
                    foreach (DataGridViewRow row in TableEditorDataGrid.Rows)
                    {
                        row.Selected = selectedIds.Exists(elm => elm == Convert.ToInt32(row.Cells[0].Value));
                    }

                    break;
                case MapWindow.Interfaces.SelectionOperation.SelectAdd:
                    foreach (DataGridViewRow row in TableEditorDataGrid.Rows)
                    {
                        if (selectedIds.Exists(elm => elm == Convert.ToInt32(row.Cells[0].Value)))
                        {
                            row.Selected = true;
                        }
                    }

                    break;

                case MapWindow.Interfaces.SelectionOperation.SelectExclude:
                    foreach (DataGridViewRow row in TableEditorDataGrid.Rows)
                    {
                        if (selectedIds.Exists(elm => elm == Convert.ToInt32(row.Cells[0].Value)))
                        {
                            row.Selected = false;
                        }
                    }

                    break;
                case MapWindow.Interfaces.SelectionOperation.SelectInvert:
                    foreach (DataGridViewRow row in TableEditorDataGrid.Rows)
                    {
                        if (selectedIds.Exists(elm => elm == Convert.ToInt32(row.Cells[0].Value)))
                        {
                            row.Selected = !row.Selected;
                        }
                    }

                    break;
            }
        }

        /// <summary>Open form to perform a query</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuQuery_Click(object sender, EventArgs e)
        {
            this.QueryForm();
        }

        /// <summary>Open form to perform a query</summary>
        private void QueryForm()
        {
            if (((DataTable)TableEditorDataGrid.DataSource).DefaultView.RowFilter != string.Empty)
            {
                ShowSelected();
            }

            frmQueryBuilder queryBuilder = new frmQueryBuilder((DataTable)TableEditorDataGrid.DataSource, this.Query);
            queryBuilder.ShowDialog();
        }

        /// <summary>Open form to perform a query</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void tbbQuery_Click(object sender, EventArgs e)
        {
            this.QueryForm();
        }

        /// <summary>Import fielddefinitions</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuImportFieldDefinitions_Click(object sender, EventArgs e)
        {
            this.ImportFieldsFromDBF();
        }

        /// <summary>Import fielddefinitions</summary>
        private void ImportFieldsFromDBF()
        {
            if (this.boShapeFile.IsReadOnly)
            {
                MessageBox.Show("Shapefile is readonly.");
            }
            else
            {
                OpenFileDialog dialog = new OpenFileDialog();
                dialog.Filter = "DBF Files (*.dbf)|*.dbf";
                dialog.Title = "Please choose the DBF file whose field definitions you would like to import...";

                if (dialog.ShowDialog() == DialogResult.OK)
                {
                    MapWinGIS.Table impTable = new MapWinGIS.Table();

                    if (!impTable.Open(dialog.FileName, null))
                    {
                        MessageBox.Show(@"The table could not be opened.");
                    }
                    else
                    {
                        Field field = null;

                        for (int i = 0; i < impTable.NumFields; i++)
                        {
                            bool skipField = false;
                            foreach (DataGridViewColumn column in TableEditorDataGrid.Columns)
                            {
                                field = impTable.get_Field(i);

                                if (column.Name == field.Name)
                                {
                                    MessageBox.Show(string.Format("The field {0} already exists and will be skipped!", field.Name));
                                    skipField = true;
                                }
                            }

                            if (!skipField)
                            {
                                BOShapeData.AddDataColumn((DataTable)TableEditorDataGrid.DataSource, field.Name, field.Type.ToString(), field.Precision.ToString(), field.Width);
                            }
                        }
                    }
                }
            }
        }

        /// <summary>Import fielddefinitions</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void btnImportFieldsFromDBF_Click(object sender, EventArgs e)
        {
            this.ImportFieldsFromDBF();
        }

        /// <summary>Open form for fieldcalculator</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuFieldCalculator_Click(object sender, EventArgs e)
        {
            this.FieldCalculator();
        }

        /// <summary>Open form for fieldcalculator</summary>
        private void FieldCalculator()
        {
            // Check if data has changed
            if (this.CheckAndSaveChanges())
            {
                frmFieldCalculator calculator = new frmFieldCalculator(this.boShapeFile, TableEditorDataGrid);
                calculator.ShowDialog();

                if (calculator.ShowTextEditor)
                {
                    new frmTextCalculator(this.boShapeFile, TableEditorDataGrid).ShowDialog();
                }
            }
            else
            {
                MessageBox.Show(@"Changes must be saved first.");
            }
        }

        /// <summary>Open form for fieldcalculator</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void btnFieldCalculator_Click(object sender, EventArgs e)
        {
            this.FieldCalculator();
        }

        /// <summary>Generate or update shapeId's</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuGenerateOrUpdateShapeID_Click(object sender, EventArgs e)
        {
            if (this.boShapeFile.IsReadOnly)
            {
                MessageBox.Show("Shapefile is readonly.");
            }
            else
            {
                this.GenerateShapeIDField("MWShapeID");
            }
        }

        /// <summary>Generate or update shapeId's</summary>
        /// <param name = "destField">The field where the id's will be written to.</param>
        private void GenerateShapeIDField(string destField)
        {
            string status = "updated";
            int idColumnNr = -1;

            DataTable dt = (DataTable)TableEditorDataGrid.DataSource;

            for (int i = 0; i < dt.Columns.Count; i++)
            {
                if (dt.Columns[i].ColumnName.ToLower() == destField.ToLower())
                {
                    idColumnNr = i;
                    break;
                }
            }

            if (idColumnNr == -1)
            {
                status = "created";

                BOShapeData.AddDataColumn(dt, destField, "Integer", "10", 10);
                idColumnNr = dt.Columns.Count - 1;
            }

            for (int j = 0; j < dt.Rows.Count; j++)
            {
                dt.Rows[j][idColumnNr] = j;
            }

            MessageBox.Show(string.Format("The {0} field has been {1}.", destField, status));
        }

        /// <summary>Open form to import external data</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuJoinExtData_Click(object sender, EventArgs e)
        {
            frmJoinExtData frmExtData = new frmJoinExtData((DataTable)this.TableEditorDataGrid.DataSource);
            frmExtData.ShowDialog();
        }

        /// <summary>Copy shapeId's</summary>
        /// <param name = "sender">The sender of the event.</param>
        /// <param name = "e">The arguments.</param>
        private void mnuCopyShapeIDs_Click(object sender, EventArgs e)
        {
            string result = string.Empty;
            FormUtils.InputBox("Assign Feature ID to a field", "Enter the name of the target field  (Data will be overwritten)", ref result);

            if (result == string.Empty)
            {
                MessageBox.Show("No field name was specified. Aborting.");
            }
            else if (result.Length > 10)
            {
                MessageBox.Show(@"The field name can only be 10 characters long. Aborting.");
            }
            else
            {
                this.GenerateShapeIDField(result);
            }
        }

        /// <summary>Menu item click event</summary>
        /// <param name="sender">The sender</param>
        /// <param name="e">The event arguments</param>
        private void UpdateMeasurementsToolStripMenuItemClick(object sender, EventArgs e)
        {
            this.OpenUpdateMeasurmentsForm();
        }

        /// <summary>Button click event</summary>
        /// <param name="sender">The sender</param>
        /// <param name="e">The event arguments</param>
        private void UpdateMeasurementsClick(object sender, EventArgs e)
        {
            this.OpenUpdateMeasurmentsForm();
        }

        /// <summary>Opens the form to update the measurements</summary>
        /// <remarks>Added by Paul Meems, 29 May 2012</remarks>
        private void OpenUpdateMeasurmentsForm()
        {
            if (this.boShapeFile.IsReadOnly)
            {
                MessageBox.Show(@"Shapefile is readonly.");
            }
            else
            {
                var dt = TableEditorDataGrid.DataSource as DataTable;

                var frm = new UpdateMeasurementsForm(dt, this.boShapeFile) { TopMost = true };
                frm.ShowDialog();
            }
        }

        private void TableEditorDataGrid_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {

            if (e.Button == MouseButtons.Right)
            {
                selectColumnIndex = e.ColumnIndex;

                System.Drawing.Point p = new System.Drawing.Point(e.X, e.Y);

                mnuGridView.Show(TableEditorDataGrid, p);

                Type type = TableEditorDataGrid[e.ColumnIndex, 0].ValueType;

                mnuStatistics.Enabled = (type == typeof(int) || type == typeof(double));
            }
        }

        private void mnuCalcValues_Click(object sender, EventArgs e)
        {
            this.FieldCalculator();
        }

        private void mnuAssignValues_Click(object sender, EventArgs e)
        {
            AssignValues();
        }

        /// <summary>Allow the user to specify a value for all of the selected records in the active column</summary>
        private void AssignValues()
        {
            string value = String.Empty;

            DialogResult result = FormUtils.InputBox("Assign Values (Selected Records)", "Input:  (No Quotes Needed for String Values)", ref value);

            if (result == DialogResult.OK)
            {
                try
                {
                    bool isValidValue = this.CheckCurrentColumn(selectColumnIndex, value);

                    if (isValidValue)
                    {
                        for (int i = 0; i < TableEditorDataGrid.Rows.Count; i++)
                        {
                            if (TableEditorDataGrid.Rows[i].Selected)
                            {
                                ///////
                                // There seems to be a bug in the datatable, if only the first record is changed it doesn't
                                // get the status modified, so force it here!!

                                DataTable dt = (DataTable)TableEditorDataGrid.DataSource;

                                if (i == 0 && dt.Rows[0].RowState != DataRowState.Modified)
                                {
                                    dt.Rows[0].SetModified();
                                }

                                //////

                                TableEditorDataGrid[selectColumnIndex, i].Value = value;
                            }
                        }
                    }
                    else
                    {
                        MessageBox.Show("Syntax Error", "Syntax Error");
                    }
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Error", ex.Message);
                }
            }
        }

        private void mnuTrimAll_Click(object sender, EventArgs e)
        {
            TrimColumn(false);
        }

        private void mnuTrimSelected_Click(object sender, EventArgs e)
        {
            TrimColumn(true);
        }

        private void TrimColumn(bool selectedOnly)
        {
            // Get column type
            Type type = TableEditorDataGrid[selectColumnIndex, 0].ValueType;

            if (type == typeof(string))
            {
                for (int i = 0; i < TableEditorDataGrid.Rows.Count; i++)
                {
                    if (!selectedOnly || TableEditorDataGrid.Rows[i].Selected)
                    {
                        TableEditorDataGrid[selectColumnIndex, i].Value = TableEditorDataGrid[selectColumnIndex, i].Value.ToString().Trim();
                    }
                    
                }
            }
        }

        private void mnuStatistics_Click(object sender, EventArgs e)
        {
            ShowStatistics();
        }

        private void ShowStatistics()
        {
            List<double> values = new List<double>();

            for (int i = 0; i < TableEditorDataGrid.Rows.Count; i++)
            {
                if (TableEditorDataGrid.Rows[i].Selected)
                {
                    values.Add(Convert.ToDouble(TableEditorDataGrid[selectColumnIndex, i].Value));
                }
            }

            if (values.Count == 0)
            {
                MessageBox.Show("No rows selected");
            }
            else
            {
                double sum = values.Sum();
                double count = values.Count;
                double min = values.Min();
                double max = values.Max();

                double mean = sum / count;
                double range = max - min;

                double sum_mean_diff = 0;

                foreach (double value in values)
                {
                    sum_mean_diff += Math.Pow((value - mean), 2);
                }

                double stdev = Math.Sqrt(sum_mean_diff / 999);

                string summary = string.Format("Column: {0}\r\nSum: {1}\r\nCount: {2}\r\nMean: {3}\r\nMaximum: {4}\r\nMinimum: {5}\r\nRange: {6}\r\nStandard Deviation: {7}"
                    , TableEditorDataGrid.Columns[selectColumnIndex].HeaderText
                    , Math.Round(sum, 2)
                    , count
                    , Math.Round(mean, 2)
                    , Math.Round(max, 2)
                    , Math.Round(min, 2)
                    , Math.Round(range, 2)
                    , Math.Round(stdev, 2));

                MessageBox.Show(summary);
            }
        }
    }
}
